/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package mathma.core.print;

import mathma.core.expr.Expression;

import mathma.core.form.FormulaicExpression;

import mathma.core.value.Value;

import java.util.HashMap;
import java.util.Map;

/**
 * @since 0.2.6
 * @author Ethan Levine
 */
public abstract class AbstractPrinter {

    private final Map<Class, Printer> printers;

    protected AbstractPrinter() {
        printers = new HashMap<Class, Printer>();
    }

    final public String print(Expression expr) {
        Class exprClass = expr.getClass();
        if (printers.containsKey(exprClass)) {
            return printers.get(exprClass).printSingle(expr);
        } else {
            return "<??:" + expr.toString() + ">";
        }
    }

    final public void link(Class c, Printer p) {
        if (!printers.containsKey(c)) {
            printers.put(c, p);
        } else {
            throw new IllegalStateException(
                    "Attempt to link an already-linked class in a printer.");
        }
    }

    final public void link(Class c, Class csrc) {
        this.link(c, printers.get(csrc));
    }

    protected abstract class Printer<E extends Expression> {
        public String printSingle(Expression expr) {
            return prints((E) expr);
        }

        abstract protected String prints(E expr);
    }

    protected class FormPrinter<O extends Value>
            extends Printer<FormulaicExpression<?,O>> {

        private final String prefix;
        private final String middle;
        private final String suffix;
        private final boolean paren;

        public FormPrinter(String prefix, String middle, String suffix,
                boolean parenthesize) {
            this.prefix = prefix;
            this.middle = middle;
            this.suffix = suffix;
            paren = parenthesize;
        }

        public FormPrinter(String prefix, String suffix, boolean parenthesize) {
            this(prefix, "", suffix, parenthesize);
        }

        public FormPrinter(String prefix, String suffix) {
            this(prefix, suffix, false);
        }

        public FormPrinter(String prefix) {
            this(prefix, ")");
        }

        protected String prints(FormulaicExpression<?,O> expr) {
            String running = "";
            for (Expression<O> o : expr) {
                if (running.equals("")) {
                    running = prefix + (paren ? "(" : "") + print(o) +
                            (paren ? ")" : "");
                } else {
                    running = running + middle + (paren ? "(" : "") + print(o) +
                            (paren ? ")" : "");
                }
            }
            return running + suffix;
        }
    }
}
